using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using Microsoft.Win32;
using MongoDB;
using MongoDB.GridFS;
using SkynetMongoDBAccess.Converter;

namespace SkynetMongoDBAccess;

public abstract class MongoAbstractDB : IDisposable
{
	private string _connectionString = "mongodb://localhost";

	private string _databaseName = "myDatabase";

	private long _fileRWLength = 2048L;

	private Mongo _mongo;

	private MongoDatabase _mongoDatabase;

	public string ServerName { get; private set; }

	public string DBName => _databaseName;

	public string CollectionName { get; protected set; }

	public long RWFileCacheMaxLength => _fileRWLength;

	public virtual MongoDatabase GetDataBase
	{
		get
		{
			if (_mongoDatabase == null)
			{
				_mongoDatabase = _mongo.GetDatabase(_databaseName) as MongoDatabase;
			}
			return _mongoDatabase;
		}
	}

	public MongoAbstractDB(string connectionString)
	{
		_connectionString = connectionString;
		string[] source = connectionString.Split(';');
		_databaseName = source.Where((string o) => o.ToLower().StartsWith("database")).First().Substring(9);
		ServerName = source.Where((string o) => o.ToLower().StartsWith("server")).First().Substring(7);
	}

	public MongoAbstractDB(string connection, long fileCacheMaxLength)
		: this(connection)
	{
		if (fileCacheMaxLength > _fileRWLength)
		{
			_fileRWLength = fileCacheMaxLength;
		}
	}

	public void Open()
	{
		if (_mongo == null)
		{
			_mongo = GetMongo(_connectionString);
		}
		if (!_mongo.TryConnect())
		{
			throw new Exception("数据库连接异常");
		}
		GetUser(DBName);
	}

	protected void Open(string dbname)
	{
		if (string.IsNullOrEmpty(dbname))
		{
			dbname = _databaseName;
		}
		if (_mongo == null)
		{
			_databaseName = dbname;
			_mongo = GetMongo(_connectionString);
		}
		if (DBName != dbname)
		{
			_mongoDatabase = _mongo.GetDatabase(dbname) as MongoDatabase;
		}
	}

	protected virtual Mongo GetMongo(string connection)
	{
		if (_mongo == null)
		{
			_mongo = new Mongo(_connectionString);
		}
		return _mongo;
	}

	public List<UserEntity> GetUser(string dbname)
	{
		if (string.IsNullOrEmpty(dbname))
		{
			dbname = _databaseName;
		}
		List<UserEntity> list = new List<UserEntity>();
		IEnumerable<Document> documents = _mongo.GetDatabase(dbname).Metadata.ListUsers().Documents;
		foreach (Document item in documents)
		{
			list.Add(item.ConvertToUserEntity());
		}
		return list;
	}

	public void AddUser(string dbname, string username, string pwd)
	{
		if (string.IsNullOrEmpty(dbname))
		{
			dbname = _databaseName;
		}
		_mongo.GetDatabase(dbname).Metadata.AddUser(username, pwd);
	}

	public void RemoveUser(string dbname, string username)
	{
		if (string.IsNullOrEmpty(dbname))
		{
			dbname = _databaseName;
		}
		_mongo.GetDatabase(dbname).Metadata.RemoveUser(username);
	}

	public List<string> GetDataBases()
	{
		List<string> list = new List<string>();
		foreach (IMongoDatabase database in _mongo.GetDatabases())
		{
			list.Add(database.Name);
		}
		return list;
	}

	public List<string> GetCollecton(string databaseName)
	{
		if (string.IsNullOrEmpty(databaseName))
		{
			databaseName = _databaseName;
		}
		List<string> collectionNames = _mongo.GetDatabase(databaseName).GetCollectionNames();
		try
		{
			string[] systemdata = new string[2] { "fs", "system" };
			return (from o in collectionNames
				select o.Split('.')[1] into o
				where !systemdata.Contains(o)
				select o).Distinct().ToList();
		}
		catch
		{
			return collectionNames;
		}
	}

	public bool DropDataBase(string databaseName)
	{
		if (string.IsNullOrEmpty(databaseName))
		{
			databaseName = _databaseName;
		}
		return _mongo.GetDatabase(databaseName).Metadata.DropDatabase();
	}

	public bool DropCollection(string databaseName, string collectionName)
	{
		if (string.IsNullOrEmpty(databaseName))
		{
			databaseName = _databaseName;
		}
		foreach (string index in GetIndexs(databaseName, collectionName))
		{
			DropIndex(databaseName, collectionName, index);
		}
		return _mongo.GetDatabase(databaseName).Metadata.DropCollection(collectionName);
	}

	public List<string> GetIndexs(string databaseName, string collectionName)
	{
		List<string> list = new List<string>();
		foreach (string key in _mongo.GetDatabase(databaseName).GetCollection(collectionName).Metadata.Indexes.Keys)
		{
			list.Add(key);
		}
		return list;
	}

	public void CreateIndex(IDictionary<string, object> filter, bool isOnly)
	{
		Document fieldsAndDirections = new Document(filter);
		_mongo.GetDatabase(DBName).GetCollection(CollectionName).Metadata.CreateIndex(fieldsAndDirections, isOnly);
	}

	public void DropIndex(string indexName)
	{
		if (indexName != "_id_")
		{
			_mongo.GetDatabase(DBName).GetCollection(CollectionName).Metadata.DropIndex(indexName);
		}
	}

	private void DropIndex(string databaseName, string collectionName, string indexName)
	{
		if (indexName != "_id_")
		{
			_mongo.GetDatabase(databaseName).GetCollection(collectionName).Metadata.DropIndex(indexName);
		}
	}

	public bool GridFsExists(string fileName)
	{
		GridFile gridFile = new GridFile(GetDataBase);
		return gridFile.Exists(fileName);
	}

	public string GridFsSaveService(string fileName, byte[] byteFile, bool isAppend)
	{
		GridFile gridFile = new GridFile(GetDataBase);
		FileMode mode = FileMode.Create;
		if (isAppend)
		{
			mode = FileMode.Append;
		}
		using GridFileStream gridFileStream = gridFile.Create(fileName, mode, FileAccess.Write);
		gridFileStream.Write(byteFile, 0, byteFile.Length);
		return fileName;
	}

	public string GridFsSave(string fileName, byte[] byteFile)
	{
		return GridFsSaveService(fileName, byteFile, isAppend: false);
	}

	public string GridFsSave(string filePath, Action<long, long> progressAction, bool isRename)
	{
		if (!File.Exists(filePath))
		{
			throw new Exception("文件不存在");
		}
		GridFile gridFile = new GridFile(GetDataBase);
		string text = Path.GetFileName(filePath);
		if (isRename)
		{
			string[] array = text.Split('.');
			text = array[0].ToString() + DateTime.Now.ToString("yyyyMMddHHmmss") + "." + array[1].ToString();
		}
		using (GridFileStream gridFileStream = gridFile.Create(text))
		{
			gridFileStream.GridFileInfo.ContentType = GetTypeContent(text);
			byte[] array2 = new byte[_fileRWLength];
			for (int i = 0; i < 5; i++)
			{
				try
				{
					using FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
					long num = 0L;
					long length = fileStream.Length;
					while (true)
					{
						int num2 = fileStream.Read(array2, 0, array2.Length);
						if (num2 <= 0)
						{
							break;
						}
						gridFileStream.Write(array2, 0, num2);
						gridFileStream.Flush();
						num += num2;
						progressAction?.Invoke(num, length);
					}
					if (num != length)
					{
						throw new Exception("文件写入错误，长度不匹配");
					}
				}
				catch
				{
					Thread.Sleep(1000);
					continue;
				}
				break;
			}
		}
		return text;
	}

	public void BeginGridFsSave(string fileName, byte[] byteFile, Action<string> callback)
	{
		Func<string, byte[], string> func = GridFsSave;
		func.BeginInvoke(fileName, byteFile, delegate(IAsyncResult async)
		{
			Func<string, byte[], string> func2 = async.AsyncState as Func<string, byte[], string>;
			string obj = func2.EndInvoke(async);
			if (callback != null)
			{
				callback(obj);
			}
		}, func);
	}

	public void BeginGridFsSave(string filePath, Action<long, long> progressAction, bool isRename, Action<string> callback)
	{
		Func<string, Action<long, long>, bool, string> func = GridFsSave;
		func.BeginInvoke(filePath, progressAction, isRename, delegate(IAsyncResult async)
		{
			Func<string, Action<long, long>, string> func2 = async.AsyncState as Func<string, Action<long, long>, string>;
			string obj = func2.EndInvoke(async);
			if (callback != null)
			{
				callback(obj);
			}
		}, func);
	}

	public byte[] GridFsReadService(string fileName, int index, int count, out long fileLength)
	{
		fileLength = 0L;
		GridFile gridFile = new GridFile(GetDataBase);
		if (gridFile.Exists(fileName))
		{
			using (GridFileStream gridFileStream = gridFile.OpenRead(fileName))
			{
				fileLength = gridFileStream.Length;
				byte[] array = new byte[fileLength];
				gridFileStream.Read(array, index, count);
				return array;
			}
		}
		return null;
	}

	public byte[] GridFsRead(string fileName)
	{
		GridFile gridFile = new GridFile(GetDataBase);
		if (gridFile.Exists(fileName))
		{
			using (GridFileStream gridFileStream = gridFile.OpenRead(fileName))
			{
				byte[] array = new byte[gridFileStream.Length];
				gridFileStream.Read(array, 0, array.Length);
				return array;
			}
		}
		return null;
	}

	public byte[] GridFsRead(string fileName, Action<long, long> progressAction)
	{
		try
		{
			GridFile gridFile = new GridFile(GetDataBase);
			if (gridFile.Exists(fileName))
			{
				using (GridFileStream gridFileStream = gridFile.OpenRead(fileName))
				{
					long length = gridFileStream.Length;
					byte[] array = new byte[length];
					if (_fileRWLength > length)
					{
						gridFileStream.Read(array, 0, array.Length);
					}
					else
					{
						long num = 0L;
						byte[] array2 = new byte[_fileRWLength];
						while (true)
						{
							int num2 = gridFileStream.Read(array2, 0, array2.Length);
							if (num2 <= 0)
							{
								break;
							}
							Array.Copy(array2, 0L, array, num, num2);
							num += num2;
							progressAction?.Invoke(num, length);
						}
					}
					return array;
				}
			}
		}
		catch
		{
		}
		return null;
	}

	public void BeginGridFsRead(string fileName, Action<long, long> progressAction, Action<byte[]> callback)
	{
		if (callback != null)
		{
			Func<string, Action<long, long>, byte[]> func = GridFsRead;
			func.BeginInvoke(fileName, progressAction, delegate(IAsyncResult asyn)
			{
				Func<string, Action<long, long>, byte[]> func2 = asyn.AsyncState as Func<string, Action<long, long>, byte[]>;
				byte[] obj = func2.EndInvoke(asyn);
				callback(obj);
			}, func);
		}
	}

	public void BeginGridFsRead(string fileName, Action<byte[]> callback)
	{
		if (callback != null)
		{
			Func<string, byte[]> func = GridFsRead;
			func.BeginInvoke(fileName, delegate(IAsyncResult asyn)
			{
				Func<string, byte[]> func2 = asyn.AsyncState as Func<string, byte[]>;
				byte[] obj = func2.EndInvoke(asyn);
				callback(obj);
			}, func);
		}
	}

	public void GridFsContainsDelete(string columnName, string valueName)
	{
		GridFile gridFile = new GridFile(GetDataBase);
		gridFile.Delete(new Document(columnName, new MongoRegex($"{valueName}")));
	}

	public void GridFsDelete(string columnName, string valueName)
	{
		GridFile gridFile = new GridFile(GetDataBase);
		gridFile.Delete(new Document("$where", $"this.{columnName} == \"{valueName}\""));
	}

	public void GridFsDeleteAll()
	{
		GridFile gridFile = new GridFile(GetDataBase);
		IEnumerable<Document> documents = gridFile.ListFiles().Documents;
		foreach (Document item in documents)
		{
			GridFsDelete("filename", item["filename"].ToString());
		}
	}

	public List<GridFileEntity> GridFsAll()
	{
		GridFile gridFile = new GridFile(GetDataBase);
		IEnumerable<Document> documents = gridFile.ListFiles().Documents;
		List<GridFileEntity> list = new List<GridFileEntity>();
		foreach (Document item in documents)
		{
			list.Add(item.ConvertToGridFileEntity());
		}
		return list;
	}

	public List<GridFileEntity> GridFsAll(int index, int count, out int maxCount)
	{
		maxCount = 0;
		GridFile gridFile = new GridFile(GetDataBase);
		ICursor cursor = gridFile.ListFiles();
		maxCount = cursor.Documents.Count();
		List<Document> list = cursor.Documents.Skip(index).Take(count).ToList();
		List<GridFileEntity> list2 = new List<GridFileEntity>();
		foreach (Document item in list)
		{
			list2.Add(item.ConvertToGridFileEntity());
		}
		return list2;
	}

	public List<GridFileEntity> GridFsAll(IDictionary<string, object> filter)
	{
		GridFile gridFile = new GridFile(GetDataBase);
		Document query = new Document(filter);
		ICursor cursor = gridFile.ListFiles(query);
		List<Document> list = cursor.Documents.ToList();
		List<GridFileEntity> list2 = new List<GridFileEntity>();
		foreach (Document item in list)
		{
			list2.Add(item.ConvertToGridFileEntity());
		}
		return list2;
	}

	public List<GridFileEntity> GridFsAll(IDictionary<string, object> filter, int index, int count, out int maxCount)
	{
		GridFile gridFile = new GridFile(GetDataBase);
		Document query = new Document(filter);
		maxCount = gridFile.ListFiles(query).Documents.Count();
		List<Document> list = gridFile.ListFiles(query).Skip(index).Limit(count)
			.Documents.ToList();
		List<GridFileEntity> list2 = new List<GridFileEntity>();
		foreach (Document item in list)
		{
			list2.Add(item.ConvertToGridFileEntity());
		}
		return list2;
	}

	private string GetTypeContent(string fileName)
	{
		string result = "application/octetstream";
		string extension = Path.GetExtension(fileName);
		RegistryKey registryKey = Registry.ClassesRoot.OpenSubKey(extension);
		if (registryKey != null && registryKey.GetValue("Content Type") != null)
		{
			result = registryKey.GetValue("Content Type").ToString();
		}
		return result;
	}

	public void Dispose()
	{
		if (_mongo != null)
		{
			_mongo.Disconnect();
			_mongo.Dispose();
			_mongo = null;
		}
		_mongoDatabase = null;
	}
}
